package main

import (
	"fmt"
	"strings"
)

/*
	练习二：
	1.指针
	2.关键字continue、break
	3.有返回值、无返回值函数
	4.函数中的可变参数使用
	5.匿名函数
	6.闭包
	7.关键字defer
*/

func main() {
	/*
		指针练习：
			1) 写一个程序，获取一个int变量num的地址，并显示到终端
			2) 将num的地址赋给指针 ptr , 并通过ptr去修改num的值.
	*/

	var num int = 10
	fmt.Printf("num的地址是：%v\n", &num)

	var ptr *int
	ptr = &num
	*ptr = 11
	fmt.Printf("ptr的地址是：%v\n", ptr)
	fmt.Printf("num的值是：%v\n", num)

	/*
		打印1——100之内的奇数[要求使用for循环+continue]
	*/
	for i := 0; i < 100; i++ {
		if i%2 == 0 {
			continue
		}
		fmt.Printf("奇数：%v\n", i)
	}

	/*
		从键盘读入个数不确定的整数，并判断读入的正数和负数的个数，输入为0时结束程序
	*/
	//var positiveCount int
	//var negativeCount int
	//var scanNum int
	//
	//for {
	//	fmt.Println("请输入一个整数。。。")
	//	fmt.Scanf("%d", &scanNum)
	//	if scanNum == 0 {
	//		break
	//	}
	//
	//	if scanNum > 0 {
	//		positiveCount++
	//		continue
	//	}
	//	negativeCount++
	//}
	//fmt.Printf("正数的个数为%v，负数的个数为：%v\n", positiveCount, negativeCount)

	/*
		某人有100,000元,每经过一次路口，需要交费,规则如下:
		当现金>50000时,每次交5%
		当现金<=50000时,每次交1000
		编程计算该人可以经过多少次路口,使用 for break方式完成
	*/
	var cash int = 100000
	var newCount int
	for {
		if cash > 50000 {
			cash -= cash * 5 / 100
			newCount++
		} else if cash <= 50000 && cash > 1000 {
			cash -= 1000
			newCount++
		} else {
			break
		}
	}
	fmt.Printf("可以经过%v次路口\n", newCount)

	/*
		斐波那契数
		请使用递归的方式，求出斐波那契数1,1,2,3,5,8,13...
		给你一个整数n，求出它的斐波那契数是多少？
		思路:
		1) 当n == 1 || n ==2 , 返回1
		2) 当n >= 2, 返回 前面两个数的和	f(n-1) + f(n-2)
	*/
	var n int = 4
	fmt.Printf("%v\n", fbn(n))

	/*
		求函数值
		已知 f(1)=3; f(n) = 2*f(n-1)+1;
		请使用递归的思想编程，求出 f(n)的值?
		思路:直接使用给出的表达式即可完成
	*/
	res := culTest(5)
	fmt.Printf("%v\n", res)

	/*
		猴子吃桃子问题
		有一堆桃子，猴子第一天吃了其中的一半，并再多吃了一个！以后每天猴子都吃其中的一半，然后再多吃一个。
		当到第十天时，想再吃时（还没吃），发现只有1个桃子了。问题：最初共多少个桃子？
		思路分析：
		1) 第10天只有一个桃子
		2) 第9天有几个桃子	=	(第10天桃子数量 + 1) * 2
		3) 规律: 第n天的桃子数据	peach(n) = (peach(n+1) + 1) * 2
	*/
	res = peach(1)
	fmt.Println(res)

	/*
		可变参数
		编写一个程序sum，可以求出1到多个int的和
	*/
	res = sum(1, 10, 21, 30)
	fmt.Println(res)

	/*
		匿名函数
		求两个数的和，使用匿名函数
	*/
	//方式一
	res1 := func(a int, b int) int {
		return a + b
	}(14, 15)
	fmt.Println(res1)

	//方式二
	res2 := func(a int, b int) int {
		return a + b
	}
	res3 := res2(14, 15)
	fmt.Println(res3)

	/*
		闭包：
		闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)
	*/
	//示例1
	f := AddUpper1()
	fmt.Println(f(10))
	//示例2
	f = AddUpper2()
	fmt.Println(f(5))

	/*
		请编写一个程序，具体要求如下
		1) 编写一个函数 makeSuffix(suffix string)	可以接收一个文件后缀名(比如.jpg)，并返回一个闭包
		2) 调用闭包，可以传入一个文件名，如果该文件名没有指定的后缀(比如.jpg) ,则返回 文件名.jpg , 如果已经有.jpg后缀，则返回原文件名。
		3) 要求使用闭包的方式完成
		4) strings.HasSuffix , 该函数可以判断某个字符串是否有指定的后缀。
	*/
	f2 := makeSuffix(".jpg")
	fmt.Println(f2("hello.jpg"))
	fmt.Println(f2("world"))

	/*
		defer 延时机制
	*/
	//关闭打开的文件
	//file, _ := os.OpenFile("")
	//defer file.Close()

	//释放数据库
	//connect := openDatabase()
	//defer connect()

	/*
		函数没有返回值:
		编写一个函数,从终端输入一个整数,打印出对应的金子塔
	*/
	var printNum int
	fmt.Println("请输入一个数字。。。")
	fmt.Scanf("%v", &printNum)
	printPyramid(printNum)

	/*
		编写函数,对给定的一个二维数组(3×3)转置
	*/
	arr := [3][3]int{
		{1, 2, 3},
		{4, 5, 6},
		{7, 8, 9},
	}
	result := transpose(arr)
	fmt.Print(result)

}

//斐波那契数列
func fbn(n int) int {
	if n == 1 || n == 2 {
		return 1
	} else {
		return fbn(n-1) + fbn(n-2)
	}
}

//题:求函数值
func culTest(n int) int {
	if n == 1 {
		return 3
	} else {
		return 2*culTest(n-1) + 1
	}
}

//题：猴子吃桃子问题
func peach(n int) int {
	if n > 10 || n < 0 {
		fmt.Println("输入的数字不正确。")
	}

	if n == 10 {
		return 1
	} else {
		return (peach(n+1) + 1) * 2
	}
}

//可变参数
func sum(args ...int) int {
	var sum int
	for i := 0; i < len(args); i++ {
		sum += args[i]
	}
	return sum
}

// AddUpper1 n的闭包
func AddUpper1() func(int) int {
	var n int = 11
	return func(x int) int {
		return n + x
	}
}

// AddUpper2 n的闭包
func AddUpper2() func(int) int {
	var n int = 11
	var str string = "hello"
	return func(x int) int {
		n += x
		str += string(65)
		fmt.Println(str)
		return n
	}
}

//闭包
func makeSuffix(suffix string) func(string) string {
	return func(name string) string {
		if strings.HasSuffix(name, suffix) {
			return name
		}
		return name + suffix
	}
}

//函数没有返回值
func printPyramid(n int) {
	//表示层数
	for i := 1; i <= n; i++ {
		//先打印空格
		for j := 1; j <= n-i; j++ {
			fmt.Print(" ")
		}
		//表示打印多少个“*”
		for k := 1; k <= 2*i-3; k++ {
			fmt.Print("*")
		}
		fmt.Println()
	}
	//下半部分层数
	for x := 1; x <= n-1; x++ {
		//先打印空格
		for y := 1; y <= x; y++ {
			fmt.Print(" ")
		}
		//打印“*”
		for z := 1; z <= 2*(n-x)-3; z++ {
			fmt.Print("*")
		}
		fmt.Println()
	}
}

func transpose(arr [3][3]int) [3][3]int {
	var transposed [3][3]int
	for i := 0; i < 3; i++ {
		for j := 0; j < 3; j++ {
			transposed[j][i] = arr[i][j]
		}
	}
	return transposed
}
